from pyautocad import Autocad, APoint
from sqlalchemy import create_engine
import pandas as pd
import time, os


def prove(input_action, type1):
    """
    验证输入的每个值是否可以转换成有效值
    :param type1: 数据转换的类型
    :param input_action: 输入input函数
    :return:
    """
    while True:
        try:
            value = type1(input(input_action))
        except ValueError:
            print("请输入有效数据!!!")
        else:
            return value


def paint_circle(acad, meter, deviate):
    p1 = APoint(meter, meter)
    acad.model.AddCircle(p1, meter)
    acad.model.AddCircle(p1, meter - deviate)


def paint_rectangle(acad, Point: APoint, length, width):
    """
    画出具体每个芯片，根据中心点去画
    :param length: 芯片的长
    :param width: 芯片的宽
    :param Point: 芯片矩形的中心点
    :return:
    """
    p1 = APoint(Point.x - (length / 2), Point.y - (width / 2))
    p2 = APoint(Point.x - (length / 2), Point.y + (width / 2))
    p3 = APoint(Point.x + (length / 2), Point.y - (width / 2))
    p4 = APoint(Point.x + (length / 2), Point.y + (width / 2))
    list1 = [p1, p2, p3, p4]
    # 画图
    list2 = [list1[0]]
    for point1 in list1:
        if point1.x == list2[0].x or point1.y == list2[0].y:
            continue
        else:
            list2.append(point1)

    for point1 in list2:
        for point2 in list1:
            if point1.x == point2.x and point1.y == point2.y:
                continue
            elif point1.x != point2.x and point1.y == point2.y:
                acad.model.AddLine(point1, point2)
            elif point1.x == point2.x and point1.y != point2.y:
                acad.model.AddLine(point1, point2)
            else:
                continue


def get_position(acad, type, x, y, diameter, num_dict, deviate, length1, width1, interval, flag, width_interval):
    """
    计算每个芯片中心点位置,并调用paint_rectangle
    :param interval: 最小间隔
    :param width_interval: 得到的上下间隔
    :param flag: 重新计算标志
    :param diameter: 圆直径
    :param acad: 创建的AutoCAD
    :param x: 圆心的X坐标
    :param y: 圆心的Y坐标
    :param type: 判断情况1、还是情况2
    :param num_dict: 上一步所得字典
    :param width1: 芯片宽
    :param length1: 芯片长
    :param deviate: 最小偏离值
    :return:
    """
    Point_list = []
    # 根据字典去计算每行的排布
    if type == 1:
        for i in num_dict.keys():
            loop1 = 2 * (((diameter / 2 - deviate) ** 2 - ((i + 0.5) * width1 + i * width_interval) ** 2) ** 0.5)
            length_interval1 = (loop1 - length1) / 2 if num_dict[i] == 1 else (loop1 - num_dict[i] * length1) / (num_dict[i] - 1)
            if i == list(num_dict.keys())[-1]:
                length_interval1 = (loop1 - num_dict[i] * length1) / (num_dict[i] + 1)
                length_interval1 = interval if length_interval1 < interval else length_interval1
            y1 = y - i * width1 - i * width_interval
            y2 = y + i * width1 + i * width_interval
            # 可以被整除，弦上为双数
            if num_dict[i] % 2 == 0:
                for num in range(1, int(num_dict[i] // 2) + 1):
                    x1 = x - (num - 0.5) * length1 - (num - 0.5) * length_interval1
                    x2 = x + (num - 0.5) * length1 + (num - 0.5) * length_interval1
                    if i == 0:
                        Point_list.append(APoint(x1, y))
                        Point_list.append(APoint(x2, y))
                    else:
                        Point_list.append(APoint(x1, y1))
                        Point_list.append(APoint(x1, y2))
                        Point_list.append(APoint(x2, y1))
                        Point_list.append(APoint(x2, y2))
            else:
                Point_list.append(APoint(x, y1))
                Point_list.append(APoint(x, y2))
                for num in range(0, int((num_dict[i] + 1) // 2)):
                    x1 = x - num * length1 - num * length_interval1
                    x2 = x + num * length1 + num * length_interval1
                    if i == 0:
                        Point_list.append(APoint(x1, y))
                        Point_list.append(APoint(x2, y))
                    else:
                        Point_list.append(APoint(x1, y1))
                        Point_list.append(APoint(x1, y2))
                        Point_list.append(APoint(x2, y1))
                        Point_list.append(APoint(x2, y2))

    else:
        for i in num_dict.keys():
            loop2 = 2 * (((diameter / 2 - deviate) ** 2 - ((i - 0.5) * width_interval + i * width1) ** 2) ** 0.5)
            length_interval2 = (loop2 - length1) / 2 if num_dict[i]==1 else (loop2 - num_dict[i] * length1) / (num_dict[i] - 1)
            if i == list(num_dict.keys())[-1]:
                length_interval2 = (loop2 - num_dict[i] * length1) / (num_dict[i] + 1)
                length_interval2 = interval if length_interval2 < interval else length_interval2
            y1 = y - (i - 0.5) * width1 - (i - 0.5) * width_interval
            y2 = y + (i - 0.5) * width1 + (i - 0.5) * width_interval
            # 弦上为双数
            if num_dict[i] % 2 == 0:
                for num in range(1, int(num_dict[i] // 2) + 1):
                    x1 = x - (num - 0.5) * length1 - (num - 0.5) * length_interval2
                    x2 = x + (num - 0.5) * length1 + (num - 0.5) * length_interval2
                    Point_list.append(APoint(x1, y1))
                    Point_list.append(APoint(x1, y2))
                    Point_list.append(APoint(x2, y1))
                    Point_list.append(APoint(x2, y2))
            else:
                Point_list.append(APoint(x, y1))
                Point_list.append(APoint(x, y2))
                for num in range(1, int((num_dict[i] + 1) // 2)):
                    x1 = x - num * length1 - num * length_interval2
                    x2 = x + num * length1 + num * length_interval2
                    Point_list.append(APoint(x1, y1))
                    Point_list.append(APoint(x1, y2))
                    Point_list.append(APoint(x2, y1))
                    Point_list.append(APoint(x2, y2))
    # 每点画图
    for point in Point_list:
        # print(point.x, point.y)
        paint_rectangle(acad, point, length1, width1)


def get_info_1(diameter1, length1, width1, interval, deviate, serial_number, union_number):
    """
    情况1:计算直径上的极限数量,以及每一行的极限数量，总数等
    得出满足总数=串数*并数 的num_dict
    :param interval: 间隔
    :param union_number: 并数
    :param serial_number: 串数
    :param diameter1: 半径
    :param length1: 芯片长度
    :param width1: 芯片宽度
    :param deviate: 偏离值
    :return: 直径极限数量,该情况总数,每行排布多少的字典,行数
    """
    # 求直径上的最大数量
    max_num = (diameter1 - 2 * deviate + interval) // (length1 + interval)
    max_num = int(max_num) if max_num < serial_number else serial_number
    # 直径上分布，满足数量
    if max_num >= serial_number * union_number:
        num_dict = {}
        total_num = max_num
        num_dict[0] = max_num
        return max_num, total_num, num_dict, diameter1

    # 判断满足数量的分布情况,首先加上最后一行>=需求数量,不加上最后一行<需求的数量
    # 极限的行数
    rows_num = int(((diameter1 / 2) - (width1 / 2) - deviate) // (width1 + interval))
    # 循环行数为 row 的情况，获得满足情况的num_dict
    for row in range(1, rows_num + 1):
        num_dict = {}
        total_num = max_num
        num_dict[0] = max_num
        # 当最上面一个芯片两个顶点在圆上,上边与圆仍有差距，计算时应减去
        width_interval = ((diameter1 - 2 * deviate - (2 * row + 1) * width1) / (2 * (row + 1)))
        # print(f"行数：{row},间隔:{width_interval}")
        for i in range(1, row + 1):
            num1 = (diameter1 / 2 - deviate) ** 2  # 半径-偏移的平方
            num2 = ((i + 0.5) * width1 + i * width_interval) ** 2  # 短边平方
            loop = ((num1 - num2) ** 0.5) * 2  # 弦长
            num = int((loop + interval) // (length1 + interval))
            num = num if num <= serial_number else serial_number
            total_num += 2 * num
            num_dict[i] = num
        if total_num >= serial_number * union_number and total_num - 2 * num_dict[row] < serial_number * union_number:
            decrease_num = total_num - serial_number * union_number
            if decrease_num % 2 == 0:
                num_dict[row] -= decrease_num // 2
                total_num -= decrease_num
                return max_num, total_num, num_dict, width_interval, decrease_num
    return 0, None, None, None, None


def get_info_2(diameter1, length1, width1, interval, deviate, serial_number, union_number):
    # loop1 = (((diameter1 / 2) ** 2 - ((0.5 * interval) + width1) ** 2) ** 0.5) * 2
    # # 不在直径上放芯片，修改判定规则
    # max_num = int((loop1 + interval) // (length1 + interval))
    # max_num = max_num if max_num < serial_number else serial_number
    # 极限行数
    rows_num = int(((diameter1 - 2 * deviate - interval) // (width1 + interval)) // 2)
    # 循环行数为 row 的情况，获得满足情况的num_dict
    for row in range(1, rows_num + 1):
        num_dict = {}
        total_num = 0
        # 当最上面一个芯片两个顶点在圆上,上边与圆仍有差距，计算时应减去
        width_interval = (diameter1 - 2 * deviate - (2 * row) * width1) / (2 * row + 1)
        for i in range(1, row + 1):
            num1 = (diameter1 / 2 - deviate) ** 2  # 半径-偏移的平方
            num2 = (i * width1 + (i - 0.5) * width_interval) ** 2  # 短边平方
            loop = ((num1 - num2) ** 0.5) * 2  # 弦长
            num = int((loop + interval) // (length1 + interval))
            num = num if num <= serial_number else serial_number
            total_num += 2 * num
            num_dict[i] = num
        if total_num >= serial_number * union_number and total_num - 2 * num_dict[row] < serial_number * union_number:
            decrease_num = total_num - serial_number * union_number
            if decrease_num % 2 == 0:
                num_dict[row] -= decrease_num // 2
                total_num -= decrease_num
                return 0, total_num, num_dict, width_interval, decrease_num
    return 0, None, None, None, None


def code_yz(Substrate_code):
    # 判断输入有效性
    engine = create_engine("mssql+pyodbc://pereader:pereader@172.18.65.31:1433/SortingDB?"
                           "driver=ODBC+Driver+17+for+SQL+Server", fast_executemany=True)
    data = pd.read_sql(f"SELECT * FROM RD_Substrate_Code WHERE Substrate_Code='{Substrate_code}' AND DataStatus=1",
                       engine)
    result = data[['ID', 'Substrate_Code', 'LES_mm']]
    return result


def Main(type, diameter, length1, width1, interval, deviate, serial_number, union_number):
    if not os.path.exists('./Download'):
        os.makedirs('./Download')
    time.sleep(0.5)
    # 使用系统杀死可能残余进程
    os.system('taskkill /IM acad.exe /F')
    max_num1, total_num1, num_dict1, width_interval1, flag1 = get_info_1(diameter, length1, width1, interval, deviate,
                                                                         serial_number, union_number)

    max_num2, total_num2, num_dict2, width_interval2, flag2 = get_info_2(diameter, length1, width1, interval, deviate,
                                                                         serial_number, union_number)
    print(f"情况1：极限数量：{max_num1};总数：{total_num1};\n数据字典：{num_dict1};间隔：{width_interval1}")
    print(f"情况2：极限数量：{max_num2};总数：{total_num2};\n数据字典：{num_dict2};间隔：{width_interval2}")
    # 画矩形测试
    if type == '1':
        # 自动连接上cad，只要cad是开着的，就创建了一个<pyautocad.api.Autocad> 对象。这个对象连接最近打开的cad文件。
        # 如果此时还没有打开cad，将会创建一个新的dwg文件，并自动开启cad软件
        acad = Autocad(create_if_not_exists=True, visible=False)
        paint_circle(acad, diameter / 2, deviate)
        get_position(acad, 1, diameter / 2, diameter / 2, diameter, num_dict1, deviate, length1, width1,
                     interval, flag1, width_interval1)
        acad.ActiveDocument.Application.Documents(acad.doc.Name).SaveAs("E:/Desktop/CAD/Download/PyAutoCAD_SaveAs")
    elif type == '2':
        # 自动连接上cad，只要cad是开着的，就创建了一个<pyautocad.api.Autocad> 对象。这个对象连接最近打开的cad文件。
        # 如果此时还没有打开cad，将会创建一个新的dwg文件，并自动开启cad软件
        acad = Autocad(create_if_not_exists=True, visible=False)
        paint_circle(acad, diameter / 2, deviate)
        get_position(acad, 2, diameter / 2, diameter / 2, diameter, num_dict2, deviate, length1, width1,
                     interval, flag2, width_interval2)
        acad.ActiveDocument.Application.Documents(acad.doc.Name).SaveAs("E:/Desktop/CAD/Download/PyAutoCAD_SaveAs")
    else:
        print(f"不能画图！")
    # 使用系统杀死此次进程
    os.system('taskkill /IM acad.exe /F')
    time.sleep(0.5)

# if __name__ == '__main__':
#     Main()
